package task

import (
	"bytes"
	"crypto/tls"
	"encoding/json"
	"errors"
	"fmt"
	"io"
	"io/ioutil"
	"log"
	"net/http"
	"scheduling/model"
	"time"

	"github.com/nacos-group/nacos-sdk-go/clients"
	"github.com/nacos-group/nacos-sdk-go/clients/naming_client"
	"github.com/nacos-group/nacos-sdk-go/common/constant"
	mod "github.com/nacos-group/nacos-sdk-go/model"
	"github.com/nacos-group/nacos-sdk-go/vo"
)

var Nacos = make(map[string]*NacosCloud, 0)

type NacosCloud struct {
	config  model.NacosConfigs
	servers []model.NacosServices
	client  naming_client.INamingClient
}

func NewNacosCloud(config model.NacosConfigs, servers []model.NacosServices) *NacosCloud {
	m := new(NacosCloud)
	m.config = config
	m.servers = servers
	return m
}

func (m *NacosCloud) Start() {
	clientConfig := *constant.NewClientConfig(
		constant.WithNamespaceId(m.config.NamespaceId.String), //当namespace是public时，此处填空字符串。
		constant.WithTimeoutMs(uint64(m.config.TimeoutMs.Int64)),
		constant.WithNotLoadCacheAtStart(true),
		/*constant.WithLogDir(path.Join("logs")),
		constant.WithCacheDir(path.Join("cache")),*/
		constant.WithRotateTime(m.config.RotateTime.String),
		constant.WithMaxAge(3),
		constant.WithLogLevel(m.config.LogEvel.String),
	)
	services := make([]constant.ServerConfig, 0)
	for _, sc := range m.servers {
		services = append(services, *constant.NewServerConfig(
			sc.Ip.String,
			uint64(sc.Port.Int64),
			constant.WithScheme(sc.Scheme.String),
			constant.WithContextPath(sc.ContextPath.String),
		))
	}
	var err error
	m.client, err = clients.NewNamingClient(
		vo.NacosClientParam{
			ClientConfig:  &clientConfig,
			ServerConfigs: services,
		},
	)
	if err != nil {
		panic(err)
	}
	Nacos[m.config.Pid.String] = m
}

func (m *NacosCloud) GetService(serviceName string) (*mod.Instance, error) {
	instance, err := m.client.SelectOneHealthyInstance(vo.SelectOneHealthInstanceParam{
		ServiceName: serviceName,
	})
	return instance, err
}

type CloudHandler struct {
	flow *FlowTask
}

func NewCloudHandler(flow *FlowTask) *CloudHandler {
	m := new(CloudHandler)
	m.flow = flow
	return m
}

func (m *CloudHandler) Handler(node *Node) error {
	configId := node.Content["configId"]
	serviceName := node.Content["serviceName"]
	path := node.Content["path"]
	method := node.Content["method"]
	contentType := node.Content["contentType"]
	dataType := node.Content["dataType"]
	success := node.Content["success"]
	dataKey := node.Content["dataKey"]

	cloud := Nacos[configId]
	service, err := cloud.GetService(serviceName)
	if err != nil {
		return errors.New("Nacos微服务失败：" + err.Error())
	}
	url := fmt.Sprintf("%s://%s:%d%s", cloud.servers[0].Scheme.String, service.Ip, service.Port, path)

	input := m.flow.BindInput(node.Params)
	var send io.Reader
	var getSend string
	tr := &http.Transport{
		TLSClientConfig: &tls.Config{InsecureSkipVerify: true},
	}
	cli := &http.Client{Timeout: 10 * time.Second, Transport: tr}
	switch contentType {
	case "application/json;charset=utf-8":
		bs, err := json.Marshal(input)
		if err != nil {
			log.Println("Nacos微服务：", err)
			return err
		}
		send = bytes.NewReader(bs)
	default:
		buf := bytes.Buffer{}
		num := len(input)
		for k, v := range input {
			buf.WriteString(k)
			buf.WriteString("=")
			buf.WriteString(v.(string))
			num--
			if num != 0 {
				buf.WriteString("&")
			}
		}
		getSend = buf.String()
	}
	var resp *http.Response
	if method == "GET" {
		resp, err = cli.Get(url + "?" + getSend)
	} else {
		req, err := http.NewRequest(method, url, send)
		if err != nil {
			log.Println("Nacos微服务请求创建Request失败", err)
			return err
		}
		req.Header.Set("Content-Type", contentType)
		resp, err = cli.Do(req)
	}
	if err != nil {
		log.Println("Nacos微服务请求失败", err)
		return err
	}
	defer resp.Body.Close()
	body, err := ioutil.ReadAll(resp.Body)
	if err != nil {
		log.Println("Nacos微服务读取响应数据失败", err)
		return err
	}
	if dataType == "string" {
		m.flow.outText[node.Id] = string(body)
	} else {
		if success != "" {
			result := make(map[string]interface{}, 0)
			err = json.Unmarshal(body, &result)
			if err != nil {
				log.Println("Nacos微服务读取响应数据格式转换失败", err)
				return err
			}
			ok, err := m.flow.Expression(success, "", result, nil)
			if err != nil {
				return errors.New(string(body))
			}
			if ok {
				if dataType == "list" {
					m.flow.outList[node.Id] = result[dataKey].([]map[string]interface{})
				} else if dataType == "object" {
					m.flow.out[node.Id] = result[dataKey].(map[string]interface{})
				}
			}
		} else {
			if dataType == "list" {
				result := make([]map[string]interface{}, 0)
				err = json.Unmarshal(body, &result)
				if err != nil {
					log.Println("Nacos微服务读取响应数据格式转换失败", err)
					return err
				}
				m.flow.outList[node.Id] = result
			} else if dataType == "object" {
				result := make(map[string]interface{}, 0)
				err = json.Unmarshal(body, &result)
				if err != nil {
					log.Println("Nacos微服务读取响应数据格式转换失败", err)
					return err
				}
				m.flow.out[node.Id] = result
			}
		}
	}
	return nil
}
